<template>
    <div class="_fc_struct">
        <el-button @click="onShowClick" block>{{ title }}</el-button>
        <ElDialog :title="title" v-model="visible" :close-on-click-modal="false" append-to-body>
            <div ref="editorRef" v-if="visible"></div>
            <template #footer>
                <span class="dialog-footer">
                <span class="_fc_err" v-if="err">
                    输入内容格式有误{{ err !== true ? err : '' }}</span>
                <ElButton @click="onCancel" >取 消</ElButton>
                <ElButton type="primary" @click="onOk">确 定</ElButton>
            </span>
            </template>
        </ElDialog>
    </div>
</template>

<script>
    import { defineComponent, ref, toRefs, watch, onMounted, nextTick } from 'vue'
    import CodeMirror from 'codemirror'
    import 'codemirror/lib/codemirror.css';
    import 'codemirror/mode/javascript/javascript';

    export default defineComponent({
        name: 'Struct',
        props: {
            modelValue: {
                type:[Object, Array, Function],
                default:{}
            },
            title: {
                type: String,
                default: '编辑数据'
            },
            defaultValue: {
                required: false
            },
            validate: Function,
        },
        emits: ['update:modelValue'],
        setup(props, { emit }) {
            const { modelValue, defaultValue, validate } = toRefs(props),
                editorRef = ref({}),
                cMirror = ref(),
                visible = ref(false),
                err = ref(),
                oldVal = ref();

            const onShowClick = () => {
                visible.value = true;
                nextTick(() => {
                    load()
                })
            }
            onMounted(() => {
            })
            const load = () => {
                const val = toJson(modelValue.value ? modelValue.value : defaultValue.value);
                oldVal.value = val;

                cMirror.value = CodeMirror(editorRef.value, {
                    lineNumbers: true,
                    mode: 'javascript',
                    gutters: ['CodeMirror-lint-markers'],
                    lint: true,
                    line: true,
                    tabSize: 2,
                    lineWrapping: true,
                    value: val || ''
                });
            }
            const toJson = (v) => {
                if (typeof v === 'function') {
                    return `${v}`;
                } else if (typeof v === 'object') {
                    return JSON.stringify(v, null, 2);
                } else {
                    return v;
                }
            }, onCancel = () => {
                console.log(editorRef.value)
                cMirror.value = null;
                oldVal.value = null;
                visible.value = false;
            }, onOk = () => {
                err.value = null;
                const str = cMirror.value.getValue();
                let val;
                try {
                    val = eval('(function (){return ' + str + '}())');
                } catch (e) {
                    err.value = ` (${e})`;
                    return;
                }
                if (validate.value && false === validate.value(val)) {
                    err.value = true;
                    return;
                }
                onCancel();
                if (toJson(val) !== oldVal.value) {
                    emit('update:modelValue', val);
                }
            };

            return {
                editorRef,
                visible,
                err,
                onShowClick,
                onOk,
                onCancel,
            };

        },
    });
</script>

<style>
    ._fc_struct .CodeMirror {
        height: 450px;
    }

    ._fc_struct .CodeMirror-line {
        line-height: 16px !important;
        font-size: 13px !important;
    }

    .CodeMirror-lint-tooltip {
        z-index: 2021 !important;
    }

    ._fc_struct .el-dialog__body {
        padding: 0px 20px;
    }

    ._fc_err {
        color: red;
        float: left;
        text-align: left;
        width: 65%;
    }
</style>
